/**
 * Copyright 2008 floop.pl
 * 
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 *  
 * http://www.apache.org/licenses/LICENSE-2.0 
 *  
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License. 
 */
package org.eaicompiler;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintStream;
import java.util.Iterator;
import java.util.Properties;

import org.antlr.runtime.ANTLRFileStream;
import org.antlr.runtime.CharStream;
import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.RecognitionException;
import org.eaicompiler.antlr.EventAILexer;
import org.eaicompiler.antlr.EventAIParser;
import org.eaicompiler.database.ScriptdevHibernateUtil;
import org.eaicompiler.decompiler.Decompiler;
import org.eaicompiler.sqlgen.SqlGenerator;
import org.eaicompiler.util.Util;

import com.martiansoftware.jsap.FlaggedOption;
import com.martiansoftware.jsap.JSAP;
import com.martiansoftware.jsap.JSAPException;
import com.martiansoftware.jsap.JSAPResult;
import com.martiansoftware.jsap.Switch;
import com.martiansoftware.jsap.UnflaggedOption;

/**
 * One entry for whole tool. Simplier that way, since we can use one FatJar
 * that'll contain all required libraries to run, and we can run it through java
 * -jar command.
 * 
 * @author floop.pl
 */
public class EAICompilerDecompiler {
	private static final String	VERSION							= "2.1 (SD2 667+)";
	private static final String	PARAM_INPUT						= "input";
	private static final String	PARAM_CREATURE_ID				= "id";
	private static final String	PARAM_OUTPUT					= "output";
	private static final String	PARAM_DECOMPILE					= "decompile";
	private static final String	PARAM_SCRIPT_DEV2_CONFIG_FILE	= "scriptdev2-config";
	private static final String	PARAM_HELP						= "help";
	private static final String	KEY_SCRIPT_DEV2_DATABASE_INFO	= "ScriptDev2DatabaseInfo";
	private static final String	PARAM_VERSION					= "version";
	private static final String	PARAM_MANGOS					= "mangos";
	private static final String	PARAM_INCLUDES					= "includes";

	@SuppressWarnings("unchecked")
	public static void main(String[] args) throws JSAPException, FileNotFoundException, IOException, RecognitionException {
		JSAP jsap = new JSAP();
		jsap.registerParameter(
				new Switch(PARAM_HELP)
				.setShortFlag('?')
				.setLongFlag(PARAM_HELP)
				.setHelp("Print this help file."));
		jsap.registerParameter(
				new Switch(PARAM_VERSION)
				.setShortFlag('v')
				.setLongFlag(PARAM_VERSION)
				.setHelp("Display version."));
		jsap.registerParameter(
				new FlaggedOption(PARAM_SCRIPT_DEV2_CONFIG_FILE)
				.setShortFlag('s')
				.setLongFlag(PARAM_SCRIPT_DEV2_CONFIG_FILE)
				.setHelp("ScriptDev2 config file location. Used to get the database address/credentials. If not given, the database contacted will be: 127.0.0.1;3306;root;root;scriptdev2"));
		jsap.registerParameter(
				new Switch(PARAM_DECOMPILE)
				.setShortFlag('d')
				.setLongFlag(PARAM_DECOMPILE)
				.setHelp("Switch to decompiler. Default is a compiler mode."));
		jsap.registerParameter(
				new FlaggedOption(PARAM_OUTPUT)
				.setStringParser(JSAP.STRING_PARSER)
				.setShortFlag('o')
				.setLongFlag(PARAM_OUTPUT)
				.setHelp("Output file name. If not provided, all output is done to console, which is probably not what you want."));
		jsap.registerParameter(
				new FlaggedOption(PARAM_MANGOS)
				.setStringParser(JSAP.STRING_PARSER)
				.setShortFlag('m')
				.setLongFlag(PARAM_MANGOS)
				.setHelp("Mangos database SQL output file name. If not provided, all output is done to console, which is probably not what you want."));
		jsap.registerParameter(
				new FlaggedOption(PARAM_CREATURE_ID)
				.setStringParser(JSAP.INTEGER_PARSER)
				.setRequired(false)
				.setList(true)
				.setListSeparator(',')
				.setShortFlag('i')
				.setLongFlag(PARAM_CREATURE_ID)
				.setHelp("ID (or ID's) of a creature that should be decompiled. If not given, all database will be decompiled."));
		jsap.registerParameter(
				new UnflaggedOption(PARAM_INPUT)
				.setHelp("Input file to compile. Required in compile mode."));
		jsap.registerParameter(
				new FlaggedOption(PARAM_INCLUDES)
				.setStringParser(JSAP.STRING_PARSER)
				.setShortFlag('n')
				.setRequired(false)
				.setList(true)
				.setListSeparator(',')
				.setLongFlag(PARAM_INCLUDES)
				.setHelp("Support predefined includes in decompiler. Type in pathnames to files that should be parsed for defines before decompiling. The decompiled output will use the values from includes, and also add the includes command at top of the decompiled file."));
		JSAPResult config = jsap.parse(args);
		if (!config.success()) {
			System.err.println();
			// print out specific error messages describing the problems
			// with the command line, THEN print usage, THEN print full
			// help. This is called "beating the user with a clue stick."
			for (Iterator errs = config.getErrorMessageIterator(); errs.hasNext();) {
				System.err.println("Error: " + errs.next());
			}
			System.err.println();
			System.err.println("Usage: java -jar eaicompiler.jar");
			System.err.println("                " + jsap.getUsage());
			System.err.println();
			System.err.println(jsap.getHelp());
			System.exit(1);
		}
		if (config.getBoolean(PARAM_HELP)) {
			System.err.println();
			System.err.println("Usage: java -jar eaicompiler.jar");
			System.err.println("                " + jsap.getUsage());
			System.err.println();
			System.err.println(jsap.getHelp());
			System.exit(1);
		}
		if (config.getBoolean(PARAM_VERSION)) {
			System.err.println();
			System.err.println("Version: " + VERSION);
			System.exit(1);
		}
		if (config.getString(PARAM_SCRIPT_DEV2_CONFIG_FILE) != null) {
			Properties properties = new Properties();
			properties.load(new FileInputStream(config.getString(PARAM_SCRIPT_DEV2_CONFIG_FILE)));
			ScriptdevHibernateUtil.setConnectionString(Util.unQuoteString(properties.getProperty(KEY_SCRIPT_DEV2_DATABASE_INFO, "127.0.0.1;3306;root;root;scriptdev2")));
			System.out.println("Using ScriptDev2 config file: " + config.getString(PARAM_SCRIPT_DEV2_CONFIG_FILE));
		}

		StringBuffer scriptDev2SQL = new StringBuffer();
		StringBuffer mangosSQL = new StringBuffer();
		if (config.getBoolean(PARAM_DECOMPILE)) {
			StringBuffer selector = new StringBuffer();
			int id[] = config.getIntArray(PARAM_CREATURE_ID);
			if (id.length > 0) {
				selector.append("where creatureId in (");
				for (int f = 0; f < id.length; f++) {
					selector.append(id[f]);
					if (f < id.length - 1) {
						selector.append(",");
					}
				}
				selector.append(")");
			}
			Decompiler d = new Decompiler();
			
			String includes[] = config.getStringArray(PARAM_INCLUDES);
			if(includes.length>0)
			{
				for(int f=0;f<includes.length;f++)
				{
					d.addInclude(includes[f]);
				}
			}			
			scriptDev2SQL.append(d.decompile(selector.toString()));
		} else {
			// Compile
			if (config.getString(PARAM_INPUT) == null) {
				System.err.println("You have to give the name of an input file to be compiled");
				System.exit(1);
			}
			CharStream input = new ANTLRFileStream(config.getString(PARAM_INPUT));
			EventAILexer lex = new EventAILexer(input);
			CommonTokenStream tokens = new CommonTokenStream(lex);

			EventAIParser parser = new EventAIParser(tokens);
			parser.script();

			SqlGenerator gen = new SqlGenerator(parser.scriptDefinitions);

			scriptDev2SQL.append("-- Localized texts:\n");
			scriptDev2SQL.append(gen.getLocalizedTextsSQL());
			scriptDev2SQL.append("\n");
			scriptDev2SQL.append("-- Events :\n");
			scriptDev2SQL.append(gen.getEventsTextsSQL());
			scriptDev2SQL.append("\n");
			scriptDev2SQL.append("-- Summons :\n");
			scriptDev2SQL.append(gen.getSummonsTextsSQL());
			mangosSQL.append("-- Mangos SQL creature script update:\n");
			mangosSQL.append(gen.getMangosSQL());
		}
		if (config.getString(PARAM_OUTPUT) != null) {
			// Decompile
			FileOutputStream out; // declare a file output object
			PrintStream p; // declare a print stream object
			try {
				out = new FileOutputStream(config.getString(PARAM_OUTPUT));
				// Connect print stream to the output stream
				p = new PrintStream(out);
				p.println(scriptDev2SQL.toString());
				p.close();
				System.out.println("Output written to " + config.getString(PARAM_OUTPUT));
			} catch (Exception e) {
				System.err.println("Error writing to file " + config.getString(PARAM_OUTPUT));
			}
		} else {
			System.out.println(scriptDev2SQL.toString());
		}

		if (config.getString(PARAM_MANGOS) != null) {
			if (!config.getBoolean(PARAM_DECOMPILE)) {
				FileOutputStream out; // declare a file output object
				PrintStream p; // declare a print stream object
				try {
					out = new FileOutputStream(config.getString(PARAM_MANGOS));
					// Connect print stream to the output stream
					p = new PrintStream(out);
					p.println(mangosSQL.toString());
					p.close();
					System.out.println("Mangos SQL output written to " + config.getString(PARAM_MANGOS));
				} catch (Exception e) {
					System.err.println("Error writing to file " + config.getString(PARAM_MANGOS));
				}
			} else {
				System.out.println(mangosSQL.toString());
			}
		}

	}
}
